<template>
  <div ref="cvsContainer" class="cvs-container">
    <canvas id="cvs" ref="cvs" class="canvas"></canvas>
    <div v-show="pageData.pointerShow" :style="`left:${pageData.pointerX}px;top: ${pageData.pointerY}px;`"
         class="pointer">
      <ul>
        <li>
          <p>时间:</p>
          <div>{{ dateFormat(new Date(data.dataFormat.timeType==='sec'?pageData.pointer[data.dataFormat.time]*1000:pageData.pointer[data.dataFormat.time])) }}</div>
        </li>
        <li>
          <p>价格:</p>
          <div :style="`color:${pageData.pointer.c>=data.open?'red':'green'}`">{{ pageData.pointer.c }}</div>
        </li>
        <li>
          <p>涨跌额:</p>
          <div :style="`color:${pageData.pointer.c>=data.open?'red':'green'}`">
            {{ `${(pageData.pointer.c - data.open).toFixed(2)}` }}
          </div>
        </li>
        <li>
          <p>涨跌幅:</p>
          <div :style="`color:${pageData.pointer.c>=data.open?'red':'green'}`">
            {{ `${(((pageData.pointer.c - data.open) / data.open) * 100).toFixed(2)}%` }}
          </div>
        </li>
        <li>
          <p>成交量:</p>
          <div>{{ pageData.pointer[data.dataFormat.volume] }}</div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup>
import {computed, defineProps, onMounted, reactive, ref} from "vue";

onMounted(() => {
  initLine()
})

const data = defineProps({
  listA: {
    type: Array,
    default: () => []
  },
  xAxios: {
    type: Array,
    default: () => []
  },
  dataFormat: {
    type: Object,
    default: () => {
      return {
        time: 't',
        timeType: 'sec', // sec|mill
        close: 'c',
        volume: 'v',
      }
    }
  },
  open: {
    type: Number,
    required: true,
    default: '0'
  },
  showXPoint: {
    type: Boolean,
    default: true
  },
  showYPoint: {
    type: Boolean,
    default: true
  }
});

const pageData = reactive({
  pointer: 0,
  pointerX: 0,
  pointerY: 0,
  animationTime: 500,
  pointerShow: false
});

const max = computed(() => {
  return Math.max(...data.listA.map(item => item[data.dataFormat.close]))
})

const maxVolume = computed(() => {
  return Math.max(...data.listA.map(item => item[data.dataFormat.volume]))
})

const min = computed(() => {
  return Math.min(...data.listA.map(item => item[data.dataFormat.close]))
})

const yMax = computed(() => {
  return max.value - min.value;
})


console.log("max:", max.value)
console.log("maxVolume:", maxVolume.value)
console.log("min", min.value)
console.log("yMax", yMax.value)
console.log("open", data.open)


const cvs = ref(null);
const cvsContainer = ref(null);
const initLine = () => {
  const container = cvsContainer.value
  const width = container.offsetWidth
  const height = container.offsetHeight
  cvs.value.width = width
  cvs.value.height = height
  const ctx = cvs.value.getContext('2d')

  // 获取设备像素比
  let dpr = window.devicePixelRatio || 1;
  let canvas = document.getElementById('cvs');

// 设置 canvas 的显示尺寸
  canvas.style.width = '550px';  // 设置显示宽度
  canvas.style.height = '455px'; // 设置显示高度

// 设置 canvas 实际绘图尺寸
  canvas.width = 550 * dpr;  // 绘图区域宽度
  canvas.height = 455 * dpr; // 绘图区域高度

// 缩放绘图上下文，使其适应设备像素比,如果不缩放会导致规格和我们预期的不一样,因为上面的宽高根据像素比缩放过
  ctx.scale(dpr, dpr);
  // 绘制基础框架
  ctx.beginPath()
  ctx.moveTo(50, 50)
  ctx.stroke()

  // 画x刻度
  // x轴总长550,我们用数据总长度计算出每个刻度要画多长,这里刻度数量=数据长度,例如10cm想要分成2个刻度,那就是开头和结束两个刻度,就等于10个要分成一段也就是10/(2-1)
  const xScale = 500 / (data.xAxios.length);
  for (let i = 0; i < data.xAxios.length; i++) {
    ctx.beginPath()
    ctx.strokeStyle = 'rgba(225,225,225,0.4)'
    ctx.lineWidth = 0.5
    ctx.moveTo(50 + i * xScale, 400)
    ctx.lineTo(50 + i * xScale, 50)
    ctx.stroke()
    if (data.showXPoint) {
      ctx.fillText(data.xAxios[i], 47 + i * xScale, 415,)
    }
  }

  // 画y刻度
  // (我们只显示7个y轴刻度)我们计算出最大值分成7份每份有多长
  const yScale = (yMax.value / 7);
  console.log('yScale', yScale)
  for (let i = 0; i <= 7; i++) {
    ctx.beginPath()
    ctx.strokeStyle = 'rgba(77,77,77,0.4)'
    ctx.lineWidth = 0.5
    ctx.moveTo(50, 400 - i * 50)
    ctx.lineTo(600, 400 - i * 50)
    ctx.stroke()
    let len = (i * yScale).toFixed(2)
    let yValue = (min.value + len / 1).toFixed(3)
    ctx.fillText(yValue, 40 - len.length * 8, 403 - i * 50,)
  }

  // 画k线
  for (let i = 0; i < data.listA.length; i++) {
    // 这里使用定时器渲染,模拟动画
    setTimeout(() => {
      let nextElm = data.listA[i + 1];
      if (!nextElm) {
        console.log('渲染结束')
        return
      }
      let price = data.listA[i][data.dataFormat.close];
      let nextPrice = nextElm[data.dataFormat.close];
      let current = price - min.value;
      let next = nextPrice - min.value;
      ctx.beginPath()
      let isIncrease = nextPrice >= data.open && price >= data.open;
      ctx.strokeStyle = isIncrease ? 'rgba(231,7,74,0.96)' : 'rgb(0,110,56)'
      ctx.lineWidth = 1
      ctx.moveTo(50 + i * xScale, 400 - current * 350 / yMax.value)
      ctx.lineTo(50 + (i + 1) * xScale, 400 - next * 350 / yMax.value)
      ctx.stroke()
      // 填充数字
      if (data.showYPoint) {
        ctx.fillText(current, 45 + i * xScale, 400 - current * 350 / yMax.value)
      }
      // 渲染渐变
      ctx.strokeStyle = isIncrease ? 'rgba(229,20,20,0.7)' : 'rgba(69,164,5,0.7)';
      ctx.moveTo(50 + i * xScale, 400 - current * 350 / yMax.value)
      ctx.lineTo(50 + (i + 1) * xScale, 400 - next * 350 / yMax.value)
      let gradient
      if (isIncrease) {
        gradient = ctx.createLinearGradient(0, 50, 0, 400 - (data.open - min.value) * 350 / yMax.value)
        gradient.addColorStop(0, 'rgba(255,0,0,.5)')
      } else {
        gradient = ctx.createLinearGradient(0, 400, 0, 400 - (data.open - min.value) * 350 / yMax.value)
        gradient.addColorStop(0, 'rgba(0,166,0,.3)')
      }
      gradient.addColorStop(1, 'rgba(255,255,255,0)');
      ctx.fillStyle = gradient
      ctx.lineTo(50 + (i + 1) * xScale, 400 - (data.open - min.value) * 350 / yMax.value)
      ctx.lineTo(50 + i * xScale, 400 - (data.open - min.value) * 350 / yMax.value)

      ctx.closePath()
      ctx.fill();
    }, pageData.animationTime / data.listA.length * i) // 渲染总时长/数据长度=每个数据渲染时长,使用定时器模拟动画
  }


  // 画成交量
  let lastPrice = 0;
  for (let i = 0; i < data.listA.length; i++) {
    // 这里使用定时器渲染,模拟动画
    setTimeout(() => {
      let volume = data.listA[i][data.dataFormat.volume];
      let price = data.listA[i][data.dataFormat.close];
      ctx.beginPath()
      ctx.strokeStyle = price > lastPrice ? 'rgba(231,7,74,0.96)' : 'rgba(0,182,92,0.98)'
      ctx.lineWidth = 550 / data.listA.length * 0.75
      // ctx.moveTo(50 + i * xScale, 400 + 5)
      // ctx.lineTo(50 + i * xScale, 400 + 5 + (50 / maxVolume.value) * volume)
      ctx.moveTo(50 + i * xScale, 450 + 5)
      ctx.lineTo(50 + i * xScale, 450 + 5 - (50 / maxVolume.value) * volume)
      ctx.stroke()
      lastPrice = price;
    }, pageData.animationTime / data.listA.length * i)
  }

  // 获取元素的边界信息
  const rect = cvs.value.getBoundingClientRect();
  // 绑定鼠标移动事件
  cvs.value.addEventListener('mousemove', (e) => {
    // 阻止浏览器默认行为
    e.preventDefault();
    // 计算鼠标在元素内部的相对位置
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    // 打印出相对位置
    if (x > 50) {
      const index = Math.round((x - 50) / xScale);
      // 四舍五入
      // 获取当前点的数据
      const value = data.listA[index];
      // 更新提示框的值
      if (value&&value[data.dataFormat.close]) {
        if (!pageData.pointerShow) {
          pageData.pointerShow = true
        }
        pageData.pointer = value
        pageData.pointerX = x + 15
        pageData.pointerY = y + 15
      } else {
        if (pageData.pointerShow) {
          pageData.pointerShow = false
        }
      }
    }
  })
  cvs.value.addEventListener('mouseleave', () => {
    pageData.pointerShow = false;
  })
  cvs.value.addEventListener('mouseenter', () => {
    pageData.pointerShow = true;
  })
}

const dateFormat = (date) => {
  let day = date.getDate();
  day = day < 10 ? '0' + day : day;
  let month = date.getMonth() + 1;
  month = month < 10 ? '0' + month : month;
  let hours = date.getHours();
  hours = hours < 10 ? '0' + hours : hours;
  let minutes = date.getMinutes();
  minutes = minutes < 10 ? '0' + minutes : minutes;
  return `${month}-${day} ${hours}:${minutes}`;
}
</script>

<style lang="scss" scoped>
.cvs-container {
  width: 100%;
  height: 100%;
  background-color: white;
  position: relative;

  .canvas {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .pointer {
    position: absolute;
    width: 130px;
    font-size: 14px;
    border-radius: 5px;
    background-color: rgba(255, 255, 255, .85);
    display: flex;
    justify-content: center;
    align-items: center;
    color: rgb(101, 98, 98);
    flex-wrap: wrap;
    word-wrap: anywhere;
    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
    padding: 10px;

    ul {
      width: 100%;
      box-sizing: border-box;
      display: flex;
      flex-direction: column;
      gap: 3px;

      li {
        font-size: 12px;
        display: flex;
        justify-content: space-between;
      }
    }
  }
}

</style>

